package com.wgo.precise.client.ui.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.views.properties.IPropertySource;


import com.wgo.bpot.client.session.DomainSessionException;
import com.wgo.bpot.domain.common.CommandHistory;
import com.wgo.bpot.server.persist.Persistent;
import com.wgo.precise.client.ui.controller.Assert;
import com.wgo.precise.client.ui.controller.ModelStatus;
import com.wgo.precise.client.ui.controller.RequirementPlugin;
import com.wgo.precise.client.ui.model.facade.IPropertiesContainer;
import com.wgo.precise.client.ui.model.facade.ITreeItem;
import com.wgo.precise.client.ui.view.editor.IModelFormPart;
import com.wgo.precise.client.ui.view.editor.properties.PropertiesFormPage;
import com.wgo.precise.client.ui.view.editor.properties.PropertiesSectionPart;
import com.wgo.precise.client.ui.view.editor.properties.PropertyLinkDescriptor;
import com.wgo.precise.client.ui.view.util.ModelItemPropertySource;

/**
 * 
 * @author Petter L. H. Eide
 * @version $Id: ClientModelItem.java,v 1.5 2006-02-19 16:29:37 petterei Exp $
 * 
 * Implements:
 *  - IAdaptable:   Used by Eclipse to get different kinds of information.
 *
 */
public abstract class ClientModelItem<T extends Persistent> implements IAdaptable, ITreeItem, IPropertiesContainer {

    private T persistent;
    private Long cachedDbId = null;
    private Class<T> cachedType = null;
    
    /**
     * The id is the propertyspecification-identifier for the PropertyValue.
     * 
     * This is called current because it only contains the values from 
     * the property-specifications from the current Configuration/CategoryType.
     * Older and obsolete property-values can be stored in another hash-map to 
     * support ie. history of an object.  
     */
    private Map<AbstractPropertySpesification,PropertyValue> currentProperties = new HashMap<AbstractPropertySpesification,PropertyValue>();

    /**
     * For display in graphical views.
     */
    private String displayImageKey;

    protected ClientModelItem() {
    	super();
    }
    protected ClientModelItem(T persistent) {
        this.persistent = persistent; 
        displayImageKey = ISharedImages.IMG_OBJ_ELEMENT;
        if (null == this.persistent.getDbId() ) {
        	markDirty();
        }
    }

	public Long getIdentifier() {
    	Long dbId = null;
    	try {
    		dbId = getDomainModelObject().getDbId();
    		cachedDbId = dbId;
    	} catch (DomainSessionException e) {
    		RequirementPlugin.logAndDisplay(new ModelStatus(IStatus.ERROR, "Error when retrieving information from Proxy", e));
    		dbId = cachedDbId;
    	}
    	return dbId; 
    }
    
	/**
	 * @see com.wgo.precise.client.ui.model.facade.IModelPersistable#getDomainObjectType()
	 */
	@SuppressWarnings("unchecked")
	public Class<T> getDomainObjectType() {
		cachedType = null != getDomainModelObject() ? null != cachedType ? cachedType : (Class<T>) getDomainModelObject().getClass().getInterfaces()[0] : null ;
		return cachedType;
	}

    /**
     * Only internally. Se variable description for further information.
     */
    protected void setPropertyValue(PropertyValue propertyValue) {
        currentProperties.put(propertyValue.getPropertySpesification(), propertyValue);
    }

    public void setPropertyDefaultValue(AbstractPropertySpesification spec) {
        Assert.notNull(spec);
        PropertyValue propertyValue = currentProperties.get(spec);
        if (propertyValue == null) {
//  TODO          currentProperties.put(spec , new PropertyValue(null , spec));
        } else {
            propertyValue.setDefaultValue();
        }
        markDirty();
    }

    /**
	 * @see com.wgo.precise.client.ui.model.facade.IPropertiesContainer#setPropertyValueFromCellEditor(com.wgo.precise.client.ui.model.AbstractPropertySpesification, java.lang.Object)
	 */
    public boolean setPropertyValueFromCellEditor(AbstractPropertySpesification spec, Object value) {
        Assert.notNull(spec);
        PropertyValue propertyValue = currentProperties.get(spec);

        boolean internalDirty = false;
        
        if (propertyValue == null) {
//       TODO     propertyValue = new PropertyValue(value , spec);
            setPropertyValue(propertyValue);
            internalDirty = true;
            markDirty();
        } else if (value != null) {
            boolean valueChanged = propertyValue.setValueFromCellEditor(value);
            internalDirty = valueChanged;
            markDirty();
        }

        return internalDirty;
    }
    
    public List<PropertyValue> getCurrentPropertyValues() {
        List<PropertyValue> listProperties = new ArrayList<PropertyValue>();
        for (PropertyValue value : currentProperties.values()) {
            listProperties.add(value);
        }
        return  listProperties;
    }
    
    public boolean isPropertySet(AbstractPropertySpesification definition){
    	return currentProperties.containsKey(definition);
    }
    
    /**
	 * @see com.wgo.precise.client.ui.model.facade.IPropertiesContainer#getCurrentPropertyValue(com.wgo.precise.client.ui.model.AbstractPropertySpesification)
	 */
    public PropertyValue getCurrentPropertyValue(AbstractPropertySpesification spec) {
        return currentProperties.get(spec);
    }

    /**
     * Internal to prevent loop.. (getCurrentPropertyValue() is overrided in 
     * extending classes. This is done to create default value if it is not set.
     * This can not be done in this abstract super-class when
     * PropertySpecifications is not reachable from this class.)
     */
    protected PropertyValue getPropertyValue(AbstractPropertySpesification spec) {
        return currentProperties.get(spec);
    }
    
    /**
     * @see com.wgo.precise.client.ui.model.facade.ILabelProviderItem#getImage()
     */
    public Image getImage() {
        return PlatformUI.getWorkbench().getSharedImages().getImage(displayImageKey);
    }

    /**
     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
        if (adapter == IPropertySource.class) {
            ModelItemPropertySource propertySource = new ModelItemPropertySource(this);
            return propertySource;
        } else if (PropertyLinkDescriptor.class == adapter) {
        	return new PropertyLinkDescriptor(this);
        }
        return null;
    }

    protected void setDisplayImageKey(String displayImageKey) {
        this.displayImageKey = displayImageKey;
    }

    public boolean isDirty() {
    	return RequirementPlugin.getInstance().getSession().getDomainSession().isDirty(getDomainModelObject());
    }
    
    public void markDirty() {
        RequirementPlugin.getInstance().getPreciseSubject().notifyModelUpdated(this);
    }
    
    public T getDomainModelObject() {
        return persistent;
    }

    /**
     *  Initialize value-wrappers for domain object-values.
     */
    protected void initPropertiesFromDomainObject(Collection<ClientPropertyDefinition<Persistent>> definitions) {
    	for (ClientPropertyDefinition<Persistent> definition : definitions) {
    		initPropertyFromDomainObject(definition);
    	}
    }
    protected void initPropertyFromDomainObject(ClientPropertyDefinition<Persistent> definition) {
    	this.setPropertyValue(new ClientPropertyValue<Persistent>(this.getDomainModelObject(), definition));
    }

    public List<IModelFormPart> getPropertiesFormParts(PropertiesFormPage page, IManagedForm managedForm) {
    	List<IModelFormPart> formParts = new ArrayList<IModelFormPart>(1);
    	Composite body = managedForm.getForm().getBody();
    	Composite section = managedForm.getToolkit().createComposite(body,SWT.NULL);
//		FillLayout layout = new FillLayout(SWT.VERTICAL);
//		layout.marginWidth = 1;
//		layout.marginHeight = 1;
//    	section.setLayout(layout);
    	formParts.add(new PropertiesSectionPart(page, section, true, this));
    	return formParts;
    }
    
	/**
	 * TODO: add method to existing interface or create new specific interface with this method
	 */
	public List<CommandHistory> getHistory() {
		return RequirementPlugin.getInstance().getSession().retrieveCommandHistory(this);
	}

    
}